package handling.channel.handler;

import java.util.List;
import java.util.Map;

import client.MapleCharacter;
import client.MapleClient;
import client.MapleQuestStatus;
import client.RockPaperScissors;
import client.inventory.Equip;
import client.inventory.IItem;
import client.inventory.ItemFlag;
import client.inventory.MapleInventoryType;
import constants.GameConstants;
import handling.SendPacketOpcode;
import scripting.NPCConversationManager;
import scripting.NPCScriptManager;
import server.AutobanManager;
import server.MapleInventoryManipulator;
import server.MapleItemInformationProvider;
import server.MapleShop;
import server.MapleStorage;
import server.life.MapleNPC;
import server.maps.MapleMap;
import server.quest.MapleQuest;
import tools.ArrayMap;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.data.output.MaplePacketLittleEndianWriter;

public class NPCHandler {
	// NPCAnimation npc 动画
	public static final void NPCAnimation(final SeekableLittleEndianAccessor slea, final MapleClient c) {
		final int length = (int) slea.available();
		if (length < 4) {
			return;
		}
		MapleMap map = c.getPlayer().getMap();
		if (map == null) {
			return;
		}
		int oid = slea.readInt();
		MapleNPC npc = map.getNPCByOid(oid);
		if (npc == null) {
			if (c.getPlayer().isAdmin()) {
				c.getPlayer().dropMessage("NPC OID =" + oid);
			}
			return;
		}

		switch (npc.getId()) {
		case 2103:
		case 10000:
		case 1010100:
		case 1012003:
		case 1012106:
		case 1032004:
		case 1052103:
		case 1061100:
			return;
		}

		if (!c.getPlayer().isMapObjectVisible(npc)) {
			return;
		}
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.writeShort(SendPacketOpcode.NPC_ACTION.getValue());

		mplew.writeInt(oid);
		if (length == 6) { // NPC 说话
			mplew.writeShort(slea.readShort());
		} else if (length > 9) { // NPC 移动
			mplew.write(slea.read(length - 13));
		} else {
			if (c.getPlayer().isAdmin()) {
				c.getPlayer().dropMessage("NPC, Packet:" + slea.toString());
			}
			return;
		}
		c.sendPacket(mplew.getPacket());
	}

	// NPCShop npc商店
	public static void NPCShop(final SeekableLittleEndianAccessor slea, final MapleClient c, final MapleCharacter chr) {
		final byte bmode = slea.readByte();
		if (chr == null) {
			return;
		}

		switch (bmode) {
		case 0: {
			final MapleShop shop = chr.getShop();
			if (shop == null) {
				return;
			}
			slea.skip(2);
			final int itemId = slea.readInt();
			final short quantity = slea.readShort();
			shop.buy(c, itemId, quantity);
			break;
		}
		case 1: {
			final MapleShop shop = chr.getShop();
			if (shop == null) {
				return;
			}
			final byte slot = (byte) slea.readShort();
			final int itemId = slea.readInt();
			final short quantity = slea.readShort();
			shop.sell(c, GameConstants.getInventoryType(itemId), slot, quantity);
			break;
		}
		case 2: {
			final MapleShop shop = chr.getShop();
			if (shop == null) {
				return;
			}
			final byte slot = (byte) slea.readShort();
			shop.recharge(c, slot);
			break;
		}
		default:
			chr.setConversation(0);
		}
	}

	// NPCTalk npc说话
	public static void NPCTalk(final SeekableLittleEndianAccessor slea, final MapleClient c, final MapleCharacter chr) {
		if ((chr == null) || (chr.getMap() == null)) {
			return;
		}
		final MapleNPC npc = chr.getMap().getNPCByOid(slea.readInt());

		slea.readInt();
		if (npc == null) {
			return;
		}
		if (chr.getConversation() != 0) {
			NPCScriptManager.getInstance().dispose(c);
			c.getSession().write(MaplePacketCreator.enableActions());

			return;
		}

		if (npc.hasShop()) {
			c.getSession().write(MaplePacketCreator.confirmShopTransaction((byte) 20));
			chr.setConversation(1);
			npc.sendShop(c);
		} else {
			NPCScriptManager.getInstance().start(c, npc.getId());
		}
	}

	// QuestAction 任务动作处理
	public static final void QuestAction(final SeekableLittleEndianAccessor slea, final MapleClient c,
			final MapleCharacter chr) {
		
		final byte action = slea.readByte();
		short quest = slea.readShort();
		if (quest < 0) { // 任务id超过 50000 会变为负数
			quest += 65536;
		}

		if (chr == null) {
			return;
		}
		if (!chr.canQuestAction()) {
			chr.dropMessage(1, "提交操作过快请稍后！");
			c.sendPacket(MaplePacketCreator.enableActions());
			return;
		}

		final MapleQuest q = MapleQuest.getInstance(quest);
		switch (action) {
		case 0: { // 恢复丢失的道具
			chr.updateTick(slea.readInt());
			final int itemid = slea.readInt();
			MapleQuest.getInstance(quest).RestoreLostItem(chr, itemid);
			if (c.getPlayer().isAdmin()) {
				c.getPlayer().dropMessage(5, "从新获得任务物品,任务ID：[" + quest + "]" + "物品ID：" + itemid);
			}
			break;
		}
		case 1: { // 开始任务
			final int npc = slea.readInt();
			q.start(chr, npc);
			if (c.getPlayer().isAdmin()) {
				c.getPlayer().dropMessage("开始任务 [" + quest + "] NPC: " + npc);
			}
			break;
		}
		case 2: { // 完成任务
			final int npc = slea.readInt();
			chr.updateTick(slea.readInt());

			if (slea.available() >= 4) {
				q.complete(chr, npc, Integer.valueOf(slea.readInt()));
			} else {
				q.complete(chr, npc);
			}
			if (c.getPlayer().isAdmin()) {
				c.getPlayer().dropMessage("完成任务 [" + quest + "] NPC: " + npc);
			}
			break;
		}
		case 3: { // 放弃任务
			if (GameConstants.canForfeit(q.getId())) {
				q.forfeit(chr);
			} else {
				chr.dropMessage(1, "你不可以放弃这个任务!");
			}
			break;
		}
		case 4: { // 脚本开始任务
			final int npc = slea.readInt();
			slea.readInt();
			NPCScriptManager.getInstance().startQuest(c, npc, quest);
			if (c.getPlayer().isAdmin()) {
				c.getPlayer().dropMessage("脚本开始任务 [" + quest + "] NPC: " + npc);
			}
			break;
		}
		case 5: { // 脚本结束任务
			final int npc = slea.readInt();
			NPCScriptManager.getInstance().endQuest(c, npc, quest, false);
			c.getSession().write(MaplePacketCreator.showSpecialEffect(9));
			chr.getMap().broadcastMessage(chr, MaplePacketCreator.showSpecialEffect(chr.getId(), 9), false);
			if (c.getPlayer().isAdmin()) {
				c.getPlayer().dropMessage("脚本完成任务 [" + quest + "] NPC: " + npc);
			}
			break;
		}
		}
	}

	// Storage 储存
	public static final void Storage(final SeekableLittleEndianAccessor slea, final MapleClient c,
			final MapleCharacter chr) {
		final MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
		final byte mode = slea.readByte();
		if (chr == null) {
			return;
		}
		final MapleStorage storage = chr.getStorage();

		switch (mode) {
		case 4: { // 取出
			final byte type = slea.readByte();
			final byte slot = storage.getSlot(MapleInventoryType.getByType(type), slea.readByte());
			final IItem item = storage.takeOut(slot);

			if (ii.isCash(item.getItemId())) {
				c.getSession().write(MaplePacketCreator.enableActions());
				return;
			}
			if (item != null) {
				if (!MapleInventoryManipulator.checkSpace(c, item.getItemId(), item.getQuantity(), item.getOwner())) {
					storage.store(item);
					chr.dropMessage(1, "你的物品栏已经满了..");
				} else {
					MapleInventoryManipulator.addFromDrop(c, item, false);
				}
				storage.sendTakenOut(c, GameConstants.getInventoryType(item.getItemId()));
			}
			break;
		}
		case 5: { // 储存道具
			final byte slot = (byte) slea.readShort();
			final int itemId = slea.readInt();
			short quantity = slea.readShort();

			if (quantity < 1) {
				return;
			}

			if (storage.isFull()) {
				c.getSession().write(MaplePacketCreator.getStorageFull());
				return;
			}

			if (chr.getMeso() < 100) {
				chr.dropMessage(1, "你沒有足够的金币买这个道具.");
			} else {
				MapleInventoryType type = GameConstants.getInventoryType(itemId);
				IItem item = chr.getInventory(type).getItem((short) slot).copy();

				if (ii.isCash(item.getItemId())) {
					c.getSession().write(MaplePacketCreator.enableActions());
					return;
				}

				if (GameConstants.isPet(item.getItemId())) {
					c.getSession().write(MaplePacketCreator.enableActions());
					return;
				}

				final byte flag = item.getFlag();

				if ((ii.isPickupRestricted(item.getItemId())) && (storage.findById(item.getItemId()) != null)) {
					c.getSession().write(MaplePacketCreator.enableActions());
					return;
				}

				if ((item.getItemId() == itemId) && ((item.getQuantity() >= quantity)
						|| (GameConstants.isThrowingStar(itemId)) || (GameConstants.isBullet(itemId)))) {
					if (ii.isDropRestricted(item.getItemId())) {
						if (ItemFlag.KARMA_EQ.check(flag)) {
							item.setFlag((byte) (flag - ItemFlag.KARMA_EQ.getValue()));
						} else if (ItemFlag.KARMA_USE.check(flag)) {
							item.setFlag((byte) (flag - ItemFlag.KARMA_USE.getValue()));
						} else {
							c.getSession().write(MaplePacketCreator.enableActions());
							return;
						}
					}
					if ((GameConstants.isThrowingStar(itemId)) || (GameConstants.isBullet(itemId))) {
						quantity = item.getQuantity();
					}
					chr.gainMeso(-100, false, true, false);
					MapleInventoryManipulator.removeFromSlot(c, type, (short) slot, quantity, false);
					item.setQuantity(quantity);
					storage.store(item);
				} else {
					AutobanManager.getInstance().addPoints(c, 1000, 0L,
							"Trying to store non-matching itemid (" + itemId + "/" + item.getItemId()
									+ ") or quantity not in posession (" + quantity + "/" + item.getQuantity() + ")");
					return;
				}
			}
			storage.sendStored(c, GameConstants.getInventoryType(itemId));
			break;
		}
		case 7: { // 银行
			int meso = slea.readInt();
			final int storageMesos = storage.getMeso();
			final int playerMesos = chr.getMeso();

			if ((meso > 0 && storageMesos >= meso) || (meso < 0 && playerMesos >= -meso)) {
				if (meso < 0 && (storageMesos - meso) < 0) { // 储存溢出
					meso = -(Integer.MAX_VALUE - storageMesos);
					if (-meso <= playerMesos) { // // 防止故障发生
						return;
					}
				} else if ((meso > 0) && (playerMesos + meso < 0)) { // 取钱溢出
					meso = Integer.MAX_VALUE - playerMesos;
					if (meso > storageMesos) { // 防止故障发生
						return;
					}
				}
				storage.setMeso(storageMesos - meso);
				chr.gainMeso(meso, false, true, false);
			} else {
				AutobanManager.getInstance().addPoints(c, 1000, 0L,
						"Trying to store or take out unavailable amount of mesos (" + meso + "/" + storage.getMeso()
								+ "/" + c.getPlayer().getMeso() + ")");
				return;
			}
			storage.sendMeso(c);
			break;
		}
		case 8: {
			storage.close();
			chr.setConversation(0);
			break;
		}
		default:
			System.out.println("未处理的存储模式 : " + mode);
		}
	}

	// MarrageNpc 结婚NPC
	public static final void MarrageNpc(MapleClient c) {
		if (c != null && c.getPlayer() != null) {
			if (c.getPlayer().getMapId() == 700000100) { // 红鸾宫
				c.getPlayer().changeMap(700000200); // 红鸾宫 - 宴客堂
			}
		}
	}

	// NPCMoreTalk npc 更多对话
	public static final void NPCMoreTalk(final SeekableLittleEndianAccessor slea, final MapleClient c) {
		final byte lastMsg = slea.readByte(); // 最后对话
		final byte action = slea.readByte(); // 00 结束对话, 01 接着对话

		final NPCConversationManager cm = NPCScriptManager.getInstance().getCM(c);

		if ((cm == null) || (c.getPlayer().getConversation() == 0) || (cm.getLastMsg() != lastMsg)) {
			return;
		}

		cm.setLastMsg((byte) -1);
		if (lastMsg == 2) {
			if (action != 0) {
				cm.setGetText(slea.readMapleAsciiString());
				if (cm.getType() == 0) {
					NPCScriptManager.getInstance().startQuest(c, action, lastMsg, -1);
				} else if (cm.getType() == 1) {
					NPCScriptManager.getInstance().endQuest(c, action, lastMsg, -1);
				} else {
					NPCScriptManager.getInstance().action(c, action, lastMsg, -1);
				}
			} else {
				cm.dispose();
			}
		} else {
			int selection = -1;
			if (slea.available() >= 4L) {
				selection = slea.readInt();
			} else if (slea.available() > 0L) {
				selection = slea.readByte();
			}
			if ((lastMsg == 4) && (selection == -1)) {
				cm.dispose();
				return;
			}
			if ((selection >= -1) && (action != -1)) {
				if (cm.getType() == 0) {
					NPCScriptManager.getInstance().startQuest(c, action, lastMsg, selection);
				} else if (cm.getType() == 1) {
					NPCScriptManager.getInstance().endQuest(c, action, lastMsg, selection);
				} else {
					NPCScriptManager.getInstance().action(c, action, lastMsg, selection);
				}
			} else {
				cm.dispose();
			}
		}
	}

	// repairAll 全部修复耐久
	public static final void repairAll(MapleClient c) {
		if (c.getPlayer().getMapId() != 240000000) { // 神木村
			return;
		}

		Equip eq;
		double rPercentage;
		int price = 0;
		Map<String, Integer> eqStats;

		final MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
		final Map<Equip, Integer> eqs = new ArrayMap<Equip, Integer>();
		final MapleInventoryType[] types = { MapleInventoryType.EQUIP, MapleInventoryType.EQUIPPED };

		for (MapleInventoryType type : types) {
			for (IItem item : c.getPlayer().getInventory(type)) {
				if (item instanceof Equip) {
					eq = (Equip) item;
					if (eq.getDurability() >= 0) {
						eqStats = ii.getEquipStats(eq.getItemId());

						if (eqStats.get("durability") > 0 && eq.getDurability() < eqStats.get("durability")) {
							rPercentage = (100.0
									- Math.ceil((eq.getDurability() * 1000.0) / (eqStats.get("durability") * 10.0)));
							eqs.put(eq, eqStats.get("durability"));
							price += (int) Math.ceil(rPercentage * ii.getPrice(eq.getItemId())
									/ (ii.getReqLevel(eq.getItemId()) < 70 ? 100.0 : 1.0));
						}
					}
				}
			}
		}
		if ((eqs.size() <= 0) || (c.getPlayer().getMeso() < price)) {
			return;
		}
		c.getPlayer().gainMeso(-price, true);
		Equip ez;
		for (Map.Entry<Equip, Integer> eqqz : eqs.entrySet()) {
			ez = (Equip) eqqz.getKey();
			ez.setDurability(((Integer) eqqz.getValue()).intValue());
			c.getPlayer().forceReAddItem(ez.copy(),
					ez.getPosition() < 0 ? MapleInventoryType.EQUIPPED : MapleInventoryType.EQUIP);
		}
	}

	// repair 修复耐久
	public static final void repair(final SeekableLittleEndianAccessor slea, final MapleClient c) {
		if ((c.getPlayer().getMapId() != 240000000) || (slea.available() < 4)) { // 神木村
			return;
		}

		final int position = slea.readInt();
		final MapleInventoryType type = position < 0 ? MapleInventoryType.EQUIPPED : MapleInventoryType.EQUIP;
		final IItem item = c.getPlayer().getInventory(type).getItem((short) (byte) position);

		if (item == null) {
			return;
		}

		final Equip eq = (Equip) item;
		final MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
		final Map<String, Integer> eqStats = ii.getEquipStats(item.getItemId());
		if ((eq.getDurability() < 0) || (((Integer) eqStats.get("durability")).intValue() <= 0)
				|| (eq.getDurability() >= ((Integer) eqStats.get("durability")).intValue())) {
			return;
		}

		final double rPercentage = (100.0
				- Math.ceil((eq.getDurability() * 1000.0) / (eqStats.get("durability") * 10.0)));

		final int price = (int) Math
				.ceil(rPercentage * ii.getPrice(eq.getItemId()) / (ii.getReqLevel(eq.getItemId()) < 70 ? 100.0 : 1.0));
		if (c.getPlayer().getMeso() < price) {
			return;
		}
		c.getPlayer().gainMeso(-price, false);
		eq.setDurability(eqStats.get("durability"));
		c.getPlayer().forceReAddItem(eq.copy(), type);
	}

	// UpdateQuest 更新任务
	public static final void UpdateQuest(final SeekableLittleEndianAccessor slea, final MapleClient c) {
		final MapleQuest quest = MapleQuest.getInstance(slea.readShort());
		if (quest != null) {
			c.getPlayer().updateQuest(c.getPlayer().getQuest(quest), true);
		}
	}

	// UseItemQuest 使用物品任务
	public static final void UseItemQuest(final SeekableLittleEndianAccessor slea, final MapleClient c) {
		final short slot = slea.readShort();
		final int itemId = slea.readInt();
		final IItem item = c.getPlayer().getInventory(MapleInventoryType.ETC).getItem(slot);
		final short qid = slea.readShort();
		slea.readShort();
		final MapleQuest quest = MapleQuest.getInstance(qid);
		final MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
		Pair<Integer, List<Integer>> questItemInfo = null;
		boolean found = false;

		for (IItem i : c.getPlayer().getInventory(MapleInventoryType.ETC)) {
			if (i.getItemId() / 10000 == 422) {
				questItemInfo = ii.questItemInfo(i.getItemId());
				if (questItemInfo != null && questItemInfo.getLeft() == qid
						&& questItemInfo.getRight().contains(itemId)) {
					found = true;
					break;
				}
			}
		}
		if (quest != null && found && item != null && item.getQuantity() > 0 && item.getItemId() == itemId) {
			final int newData = slea.readInt();
			final MapleQuestStatus stats = c.getPlayer().getQuestNoAdd(quest);
			
			if ((stats != null) && (stats.getStatus() == 1)) {
				stats.setCustomData(String.valueOf(newData));
				c.getPlayer().updateQuest(stats, true);
				MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.ETC, slot, (short) 1, false);
			}
		}
	}

	// RPSGame
	public static final void RPSGame(final SeekableLittleEndianAccessor slea, final MapleClient c) {
		if (slea.available() == 0 || !c.getPlayer().getMap().containsNPC(9000019)) { // 石头剪刀布运营员
			if (c.getPlayer().getRPS() != null) {
				c.getPlayer().getRPS().dispose(c);
			}
			return;
		}
		final byte mode = slea.readByte();
		switch (mode) {
		case 0: // 开始有效
		case 5: // 重试
			if (c.getPlayer().getRPS() != null) {
				c.getPlayer().getRPS().reward(c);
			}
			if (c.getPlayer().getMeso() >= 1000) {
				c.getPlayer().setRPS(new RockPaperScissors(c, mode));
			} else {
				c.getSession().write(MaplePacketCreator.getRPSMode((byte) 0x08, -1, -1, -1));
			}
			break;
		case 1: // 回答
			if ((c.getPlayer().getRPS() == null) || (!c.getPlayer().getRPS().answer(c, slea.readByte()))) {
				c.getSession().write(MaplePacketCreator.getRPSMode((byte) 0x0D, -1, -1, -1));
			}
			break;
		case 2: // 超时
			if ((c.getPlayer().getRPS() == null) || (!c.getPlayer().getRPS().timeOut(c))) {
				c.getSession().write(MaplePacketCreator.getRPSMode((byte) 0x0D, -1, -1, -1));
			}
			break;
		case 3: // 继续
			if ((c.getPlayer().getRPS() == null) || (!c.getPlayer().getRPS().nextRound(c))) {
				c.getSession().write(MaplePacketCreator.getRPSMode((byte) 0x0D, -1, -1, -1));
			}
			break;
		case 4: // 离开
			if (c.getPlayer().getRPS() != null) {
				c.getPlayer().getRPS().dispose(c);
			} else {
				c.getSession().write(MaplePacketCreator.getRPSMode((byte) 13, -1, -1, -1));
			}
			break;
		}
	}
}
